﻿using System;
using System.Collections.Generic;
using System.Text;
using Mesuro.Common;

namespace Mesuro.Equation {
	public class EqParser {
		public static VirtualChannelCallback Parse( String expr, out ISeries series ) {
			List<IChannelRef> refs = new List<IChannelRef>();
			List<Func<double, double, double>> ops = new List<Func<double, double, double>>();

			StringBuilder nameBuilder = new StringBuilder();

			foreach (char c in expr) {
				if (char.IsLetterOrDigit( c ) || c == '_' || c == '.')
					nameBuilder.Append( c );
				else if (nameBuilder.Length > 0) {

					refs.Add( DataSet.GetChannelRef( nameBuilder.ToString() ) );

					nameBuilder.Clear();

					if (refs.Count != ops.Count + 1)
						throw new EquationFormatException( "No operator between successive channel references." );

				} else {//Operator
					switch (c) {
						case '+':
							ops.Add( Add ); break;

						case '-':
							ops.Add( Sub ); break;

						case '*':
							ops.Add( Mul ); break;

						case '/':
							ops.Add( Div ); break;

						case ' ':
						case '\t':
							continue;

						default:
							throw new EquationFormatException( "Unexpected character in equation: " + c );
					}

					if (refs.Count != ops.Count)
						throw new EquationFormatException( "Consecutive operators not allowed!" );
				}//--Operator
			}
			if (nameBuilder.Length > 0) {

				refs.Add( DataSet.GetChannelRef( nameBuilder.ToString() ) );

				nameBuilder.Clear();

				if (refs.Count != ops.Count + 1)
					throw new EquationFormatException( "No operator between successive channel references." );

			}
			ISeries _series = series = DataSet.GetSeries( refs[0] );
			return delegate( Vector source ) {
				double acc = source[_series.Channels.IndexOf( refs[0].Channel )];
				for (int i = 1; i < refs.Count; i++) {
					acc = ops[i - 1]( acc, source[_series.Channels.IndexOf( refs[i].Channel )] );
				}
				return acc;
			};
		}

		private static double Add( double a, double b ) {
			return a + b;
		}
		private static double Sub( double a, double b ) {
			return a - b;
		}
		private static double Mul( double a, double b ) {
			return a * b;
		}
		private static double Div( double a, double b ) {
			return a / b;
		}
	}
	public class EquationFormatException : Exception {
		public EquationFormatException() : base() { }
		public EquationFormatException( string message ) : base( message ) { }
	}
}
